#pragma once

#include "sock.hpp"
#include <iostream>
#include <functional>
#include <sys/select.h>
#include <unistd.h>

using namespace std;

using func = function<string(const string&)>;

const int fdnum = sizeof(fd_set) * 8;
const int defaultfd = -1;

class selectServer
{
public:
    selectServer(uint16_t port, func f)
    : _port(port),
      _f(f),
      _listensock(-1),
      _fdarray(nullptr)
    {}

    ~selectServer()
    {
        if (_listensock > 0)
            close(_listensock);
        delete[] _fdarray;
    }

    void initServer()
    {
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, _port);
        Sock::Listen(_listensock);
        _fdarray = new int[fdnum];
        for (int i = 0; i < fdnum; i++)
            _fdarray[i] = defaultfd;
        _fdarray[0] = _listensock;
    }

    void startServer()
    {
        //struct timeval timeout = {10, 0};
        while (true)
        {
            fd_set rfds;
            FD_ZERO(&rfds);
            int maxfd = _fdarray[0];
            for (int i = 0; i < fdnum; i++)
            {
                if (_fdarray[i] == defaultfd)
                    continue;
                FD_SET(_fdarray[i], &rfds);
                if (_fdarray[i] > maxfd)
                    maxfd = _fdarray[i];
            }
            //int n = select(maxfd + 1, &rfds, nullptr, nullptr, &timeout);
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);
            switch (n)
            {
            case 0:
                cout << "time out" << endl;
                break;
            case -1:
                cout << "select error: " << strerror(errno) << endl;
                break;
            default:
                cout << "have event ready" << endl;
                handlerEvent(rfds);
                break;
            }
        }        
    }

    //{} [] () - & 
    void handlerEvent(fd_set& rfds)
    {
        for (int i = 0; i < fdnum; i++)
        {
            if (_fdarray[i] == defaultfd || !FD_ISSET(_fdarray[i], &rfds))
                continue;
            if (_fdarray[i] == _listensock)//有新连接到来
            {
                //cout << "get a new link" << endl;
                Accepter();
            }
            else//已有连接中有读事件就绪
            {
                Receiver(_fdarray[i], i);
            }
        }
    }

    void Accepter()
    {
        struct sockaddr_in client;
        memset(&client, 0, sizeof client);
        int sock = Sock::Accept(_listensock, &client);
        string clientip = inet_ntoa(client.sin_addr);
        uint16_t clientport = ntohs(client.sin_port);
        cout << "accept a new link " << clientip << "[" << clientport << "]" << endl;
        int i = 0;
        for (i; i < fdnum; i++)
        {
            if (_fdarray[i] != defaultfd)
                continue;
            else
                break;
        }
        // 判断服务器是否还有能力接收新连接 select最多同时维护1024个文件描述符
        if (i == fdnum)
        {
            cout << "server is full, please wait" << endl;
            close(sock);
        }
        else
        {
            _fdarray[i] = sock;
        }
        Print();
    }

    void Receiver(int sock, int pos)
    {
        char buffer[1024];
        int n = recv(sock, buffer, sizeof buffer, 0);
        if (n > 0)
        {
            buffer[n - 1] = 0;
            cout << "#client: " << buffer << endl;
        }
        else if (n == 0)
        {
            cout << "client quit" << endl;
            close(sock);
            _fdarray[pos] = defaultfd;
            return;
        }
        else
        {
            cout << "recv error: " << strerror(errno) << endl;
            close(sock);
            _fdarray[pos] = defaultfd;
            return;
        }
        string response = _f(buffer);
        //write(sock, response.c_str(), response.size());
        send(sock, response.c_str(), response.size(), 0);
    }

    void Print()
    {
        cout << "fdlist: ";
        for (int i = 0; i < fdnum; i++)
        {
            if (_fdarray[i] == defaultfd)
                continue;
            cout << _fdarray[i] << " ";
        }
        cout << endl;
    }


private:
    int _listensock;
    uint16_t _port;
    func _f;
    int* _fdarray;
};